﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace EventFlowLibrary.GameStructure
{
    /// <summary>
    /// Describes the way that the background texture opens to display text.
    /// </summary>
    internal enum OpeningType : short
    {
        Immediate,              // The texture is immediately fully visible
        ScrollDown,             // The texture starts max width and scrolls down
        ScrollLeft,             // The texture starts max height and scrolls left
        ScrollRight,            // The texture starts max height and scrolls right
        ScrollUp,               // The texture starts max width and scrolls up
    }

    /// <summary>
    /// Describes the way that text is delivered to the screen.
    /// </summary>
    internal enum TypeDelivery : short
    {
        Immediate,              // All the text is immediately displayed
        Type,                   // Characters are drawn one at a time, with a set interval between
    }

    /// <summary>
    /// Style components for text display.
    /// </summary>
    internal enum TypeDecoration : short
    {
        None,                   // Text displayed once
        Shadow,                 // Text has a shadow underneath
    }

    /// <summary>
    /// A series of frames, which change and display text. Can be referenced by a unique ID.
    /// </summary>
    internal sealed class TextEvent
    {
        /// <summary>
        /// The unique ID of the text event.
        /// </summary>
        internal string ID { get { return id; } }
        private readonly string id;

        /// <summary>
        /// The key for the background texture shown under the text.
        /// </summary>
        internal string BackgroundTexture { get { return backgroundTexture; } }
        private readonly string backgroundTexture;

        /// <summary>
        /// Color for displaying backgorund texture.
        /// </summary>
        internal Color BackgroundColor { get { return backgroundColor; } }
        private readonly Color backgroundColor;

        /// <summary>
        /// The key for the foreground texture shown over the text.
        /// </summary>
        internal string ForegroundTexture { get { return foregroundTexture; } }
        private readonly string foregroundTexture;

        /// <summary>
        /// The method for opening the background texture.
        /// </summary>
        internal OpeningType BackgroundOpening { get { return backgroundOpening; } }
        private readonly OpeningType backgroundOpening;

        /// <summary>
        /// How fast the background texture opens in pixels per second.
        /// </summary>
        internal int OpenSpeed { get { return openSpeed; } }
        private readonly int openSpeed;

        /// <summary>
        /// How fast the background texture closes in pixels per second.
        /// </summary>
        internal int CloseSpeed { get { return closeSpeed; } }
        private readonly int closeSpeed;

        /// <summary>
        /// The starting width or height for dimension of the scroll out openings.
        /// </summary>
        internal int StartDim { get { return startDim; } }
        private readonly int startDim;

        /// <summary>
        /// The current dimensions of the background texture.
        /// </summary>
        internal Rectangle CurrentDimensions { get { return currentDimensions; } set { currentDimensions = value; } }
        private Rectangle currentDimensions = Rectangle.Empty;

        /// <summary>
        /// The desired dimensions of the background texture when opened.
        /// </summary>
        internal Rectangle DesiredOpenDimensions { get { return desiredOpenDimensions; } }
        private Rectangle desiredOpenDimensions = Rectangle.Empty;

        /// <summary>
        /// The desired dimensions of the background texture when closed.
        /// </summary>
        internal Rectangle DesiredClosedDimensions { get { return desiredClosedDimensions; } }
        private Rectangle desiredClosedDimensions = Rectangle.Empty;

        /// <summary>
        /// The text frames composing this text event.
        /// </summary>
        internal TextFrame[] TextFrames { get { return textFrames.ToArray(); } }
        private List<TextFrame> textFrames;

        /// <summary>
        /// Create a text event.
        /// </summary>
        /// <param name="_id">The unique ID for the text event.</param>
        /// <param name="_backgroundTexture">Background texture shown under the text.</param>
        /// <param name="_backgroundColor">Color for displaying background texture.</param>
        /// <param name="_foregroundTexture">Texture to be shown on top of text once the background is totally opened.</param>
        /// <param name="_backgroundOpening">The method of opening the background texture.</param>
        /// <param name="_startDim">The starting dimension of the background texture before scrolling for scroll type openings.</param>
        /// <param name="_openSpeed">Pixel per second opening speed of background texture.</param>
        /// <param name="_closeSpeed">Pixel per second closing speed of background texture.</param>
        /// <param name="_viewport">The screen's viewport.</param>
        /// <param name="_percentDimensions">Percentage of the screen, in ints, that the textures should occupy when fully opened. 
        /// This specifies the percent of the top left corner, and the percent of the bottom right, in screen percentage coordinates.</param>
        internal TextEvent(string _id, string _backgroundTexture, Color _backgroundColor, string _foregroundTexture, OpeningType _backgroundOpening, int _startDim,
            int _openSpeed, int _closeSpeed, Viewport _viewport, Rectangle _percentDimensions)
        {
            // Assign values
            id = _id;
            backgroundTexture = _backgroundTexture;
            backgroundColor = _backgroundColor;
            foregroundTexture = _foregroundTexture;
            backgroundOpening = _backgroundOpening;
            startDim = _startDim;
            openSpeed = _openSpeed;
            closeSpeed = _closeSpeed;
            textFrames = new List<TextFrame>();

            // Set size values, based on percentage from edge of screen
            switch (backgroundOpening)
            {
                case OpeningType.Immediate:
                    // Set to max the texture out
                    desiredClosedDimensions = currentDimensions = new Rectangle((int)(_viewport.Width * (float)(_percentDimensions.Left / 100.0f)),
                                              (int)(_viewport.Height * (float)(_percentDimensions.Top / 100.0f)),
                                              (int)(_viewport.Width * (float)(_percentDimensions.Width / 100.0f)),
                                              (int)(_viewport.Height * (float)(_percentDimensions.Height / 100.0f)));
                    break;
                case OpeningType.ScrollDown:
                    desiredClosedDimensions = currentDimensions = new Rectangle((int)(_viewport.Width * (float)(_percentDimensions.Left / 100.0f)),
                                              (int)(_viewport.Height * (float)(_percentDimensions.Top / 100.0f)),
                                              (int)(_viewport.Width * (float)(_percentDimensions.Width / 100.0f)),
                                              startDim);
                    break;
                case OpeningType.ScrollLeft:
                    desiredClosedDimensions = currentDimensions = new Rectangle((int)(_viewport.Width * (float)((_percentDimensions.Left / 100.0f) + (_percentDimensions.Width / 100.0f))) - startDim,
                                              (int)(_viewport.Height * (float)(_percentDimensions.Top / 100.0f)),
                                              startDim + 1,
                                              (int)(_viewport.Height * (float)(_percentDimensions.Height / 100.0f)));;
                    break;
                case OpeningType.ScrollRight:
                    desiredClosedDimensions = currentDimensions = new Rectangle((int)(_viewport.Width * (float)(_percentDimensions.Left / 100.0f)),
                                              (int)(_viewport.Height * (float)(_percentDimensions.Top / 100.0f)),
                                              startDim,
                                              (int)(_viewport.Height * (float)(_percentDimensions.Height / 100.0f)));
                    break;
                case OpeningType.ScrollUp:
                    desiredClosedDimensions = currentDimensions = new Rectangle((int)(_viewport.Width * (float)(_percentDimensions.Left / 100.0f)),
                                              (int)(_viewport.Height * (float)((_percentDimensions.Top / 100.0f) + (_percentDimensions.Height / 100.0f))) - startDim,
                                              (int)(_viewport.Width * (float)(_percentDimensions.Width / 100.0f)),
                                              startDim);
                    break;
            }

            desiredOpenDimensions = new Rectangle((int)(_viewport.Width * (float)(_percentDimensions.Left / 100.0f)),
                                              (int)(_viewport.Height * (float)(_percentDimensions.Top / 100.0f)),
                                              (int)(_viewport.Width * (float)(_percentDimensions.Width / 100.0f)),
                                              (int)(_viewport.Height * (float)(_percentDimensions.Height / 100.0f)));
        }

        /// <summary>
        /// Construct a frame for display.
        /// </summary>
        /// <param name="_font">The font for displaying the frame's text.</param>
        /// <param name="_continueButtons">Comma delimited buttons allowing continuance.</param>
        /// <param name="_skipButtons">Whether the user can use a ContinueButton to make all the text in the frame to appear at once.</param>
        /// <param name="_buttonLocation">The location in screen coordinate percentage of where the button's center is located.</param>
        /// <param name="_buttonSize">The size of the button in pixels.</param>
        /// <param name="_autoContinueTime">Time to autocontinue without user input; -1 means indefinite.</param>
        internal void AddFrame(string _font, string _continueButtons, bool _canSkip, Vector2 _buttonLocation, Vector2 _buttonSize, int _autoContinueTime, bool _endSound)
        {
            textFrames.Add(new TextFrame(_font, _continueButtons, _canSkip, _buttonLocation, _buttonSize, _autoContinueTime, _endSound));
        }
    }

    /// <summary>
    /// A single block of text, of which only one can be shown at a time.
    /// </summary>
    internal sealed class TextFrame
    {
        /// <summary>
        /// The font used to draw the text frame.
        /// </summary>
        internal string Font { get { return font; } }
        private readonly string font;

        /// <summary>
        /// The buttons that can be used to continue to the next frame when the current frame is done.
        /// Can be empty, to disallow user continuance and rely on AutoContinueTime.
        /// </summary>
        internal Buttons[] ContinueButtons { get { return continueButtons; } }
        private readonly Buttons[] continueButtons;

        /// <summary>
        /// Whether the user can use a ContinueButton to make all the text in the frame to appear at once.
        /// </summary>
        internal bool CanSkip { get { return canSkip; } }
        private readonly bool canSkip;

        /// <summary>
        /// The location in screen coordinate percentage of where the button's center is located.
        /// </summary>
        internal Vector2 ButtonLocation { get { return buttonLocation; } }
        private readonly Vector2 buttonLocation;

        /// <summary>
        /// The size of the button in pixels.
        /// </summary>
        internal Vector2 ButtonSize { get { return buttonSize; } }
        private readonly Vector2 buttonSize;

        /// <summary>
        /// The time at which the frame continues to the next automatically.
        /// If value is -1, never auto continues.
        /// </summary>
        internal int AutoContinueTime { get { return autoContinueTime; } }
        private readonly int autoContinueTime;

        /// <summary>
        /// Whether to play a sound when the frame ends (player input, etc)
        /// </summary>
        internal bool EndSound { get { return endSound; } }
        private readonly bool endSound;

        /// <summary>
        /// The sets of text that compose this frame, which are displayed consecutively.
        /// </summary>
        internal FramePart[] FrameParts { get { return frameParts.ToArray(); } }
        private List<FramePart> frameParts;

        /// <summary>
        /// Construct a frame for display.
        /// </summary>
        /// <param name="_font">The font for displaying the frame's text.</param>
        /// <param name="_continueButtons">Comma delimited buttons allowing continuance.</param>
        /// <param name="_skipButtons">Comma delimited buttons allowing skipping.</param>
        /// <param name="_buttonLocation">The location in screen coordinate percentage of where the button's center is located.</param>
        /// <param name="_buttonSize">The size of the button in pixels.</param>
        /// <param name="_autoContinueTime">Time to autocontinue without user input; -1 means indefinite.</param>
        internal TextFrame(string _font, string _continueButtons, bool _canSkip, Vector2 _buttonLocation, Vector2 _buttonSize, int _autoContinueTime, bool _endSound)
        {
            // Assign the variables
            font = _font;
            autoContinueTime = _autoContinueTime;
            List<Buttons> cList = new List<Buttons>();
            canSkip = _canSkip;
            buttonLocation = _buttonLocation;
            buttonSize = _buttonSize;
            endSound = _endSound;
            frameParts = new List<FramePart>();

            // Assign buttons
            if (_continueButtons.Length > 0)
            {
                string[] c = _continueButtons.Split(',');
                for (int i = 0; i < c.Length; i++)
                {
                    switch (c[i])
                    {
                        case "A":
                            cList.Add(Buttons.A); break;
                        case "B":
                            cList.Add(Buttons.B); break;
                        case "X":
                            cList.Add(Buttons.X); break;
                        case "Y":
                            cList.Add(Buttons.Y); break;
                    }
                }
            }

            continueButtons = cList.ToArray();
        }

        /// <summary>
        /// Add a new FramePart to the TextFrame.
        /// </summary>
        /// <param name="_partColor">The color of the text for the FramePart.</param>
        /// <param name="_delivery">The delivery mechanism for the individual characters of the text.</param>
        /// <param name="_timeBetweenChars">The delay between typed-delivered characters.</param>
        /// <param name="_decoration">Whether the text has decoration or not.</param>
        /// <param name="_shadowOffset">Offset of shadows, if used.</param>
        /// <param name="_shadowColor">Color of shadows, if used.</param>
        /// <param name="_text">The text comprising this FramePart.</param>
        internal void AddFramePart(Color _partColor, TypeDelivery _delivery, int _timeBetweenChars, TypeDecoration _decoration, int _shadowOffset, Color _shadowColor, bool _typeSound, string _text)
        {
            frameParts.Add(new FramePart(_partColor, _delivery, _timeBetweenChars, _decoration, _shadowOffset, _shadowColor, _typeSound, _text));
        }
    }

    /// <summary>
    /// A FramePart is the text part of the display, containing the color and
    /// time delay between each segment of a TextFrame.
    /// </summary>
    internal sealed class FramePart
    {
        /// <summary>
        /// The color of the text to be displayed in this part.
        /// </summary>
        internal Color PartColor { get { return partColor; } }
        private readonly Color partColor;

        /// <summary>
        /// The delivery mechanism of the characters of the text in this part.
        /// </summary>
        internal TypeDelivery Delivery { get { return delivery; } }
        private readonly TypeDelivery delivery;

        /// <summary>
        /// Time between typing each character to the screen.
        /// </summary>
        internal int TimeBetweenChars { get { return timeBetweenChars; } }
        private readonly int timeBetweenChars;

        /// <summary>
        /// Decoration for the displayed text.
        /// </summary>
        internal TypeDecoration Decoration { get { return decoration; } }
        private readonly TypeDecoration decoration;

        /// <summary>
        /// If the text is shadowed, the text shadow offset.
        /// </summary>
        internal int ShadowOffset { get { return shadowOffset; } }
        private readonly int shadowOffset;

        /// <summary>
        /// If the text is shadowed, the text shadow color.
        /// </summary>
        internal Color ShadowColor { get { return shadowColor; } }
        private readonly Color shadowColor;

        /// <summary>
        /// Whether to play a sound on each letter stroke.
        /// </summary>
        internal bool TypeSound { get { return typeSound; } }
        private readonly bool typeSound;

        /// <summary>
        /// The text comprising this frame part.
        /// </summary>
        internal string Text { get { return text; } }
        private readonly string text;

        /// <summary>
        /// Create a new FramePart, which is a building block for a TextFrame.
        /// </summary>
        /// <param name="_partColor">The color of the text for the FramePart.</param>
        /// <param name="_delivery">The delivery mechanism for the individual characters of the text.</param>
        /// <param name="_timeBetweenChars">The delay between typed-delivered characters.</param>
        /// <param name="_decoration">Whether the text has decoration or not.</param>
        /// <param name="_shadowOffset">Offset of shadows, if used.</param>
        /// <param name="_shadowColor">Color of shadows, if used.</param>
        /// <param name="_text">The text comprising this FramePart.</param>
        internal FramePart(Color _partColor, TypeDelivery _delivery, int _timeBetweenChars, TypeDecoration _decoration, int _shadowOffset, Color _shadowColor, bool _typeSound, string _text)
        {
            // Assign the values
            partColor = _partColor;
            delivery = _delivery;
            timeBetweenChars = _timeBetweenChars;
            decoration = _decoration;
            shadowOffset = _shadowOffset;
            shadowColor = _shadowColor;
            typeSound = _typeSound;
            text = _text;
        }
    }
}